import 'dart:io';
import 'dart:utf';
import 'dart:uri';

final currentPath = new Path(new Directory.current().path);

// Modify this for your environment
final sdkPath = currentPath
                  .append('../../../eclipse-3.7.2-64/dart-sdk')
                  .canonicalize();

final patchPath = currentPath.append('lib/src/compiler/implementation/lib');

final cachePath = currentPath.append('lib/src/cache');
StringBuffer cacheDirectives = new StringBuffer('library cache;\n');
StringBuffer cacheSource = new StringBuffer('Map<String, String> CACHE = const {\n');

/**
 * Read  dart: library source and generate a constant map
 * of relative dart library paths to dart source.
 * This is used to feed dart2js with dart:* library source for compilation
 */
main() {
  setup();
  generateLib(sdkPath, 'lib/core', 'core.dart');
  generateLib(sdkPath, 'lib/coreimpl', 'coreimpl.dart');
  generatePatches().then((success) {
    var out = new File.fromPath(cachePath.append('cache.dart')).openOutputStream();
    try {
      out.writeString('$cacheDirectives\n$cacheSource\n};\n');
    } finally {
      out.close();
    }
    print('\ndone');
  });
}

/// Read and cache the specified dart: library 
generateLib(Path basePath, String libDirName, String libFileName) {
  print('');
  print('generate $libDirName/$libFileName library');
  cacheDirectives.add('\n  // $libDirName/$libFileName library\n');
  cacheSource.add('\n  // $libDirName/$libFileName library\n');
  var libDirPath = basePath.append(libDirName);
  var libFilePath = libDirPath.append(libFileName);
  for (Path partPath in extractParts(generateFile(basePath, libFilePath))) {
    generateFile(basePath, libDirPath.join(partPath));
  }
}

/// Read and cache the dart2js patch files
Future generatePatches() {
  print('\ngenerate patch files');
  cacheDirectives.add('\n  // patch files\n');
  cacheSource.add('\n  // patch files\n');
  var completer = new Completer<bool>();
  new Directory.fromPath(patchPath).list()
    ..onFile = (pathName) {generateFile(patchPath, new Path(pathName), 'patch/');}
    ..onDone = (finished) {completer.complete(finished);};
  return completer.future;
}

generatePatch(String pathName) {
  
}

/// Read and cache the specified file, then append a part directive
String generateFile(Path basePath, Path srcPath, [String prefix]) {
  var oldSrc = new File.fromPath(srcPath).readAsTextSync();
  var relPath = srcPath.relativeTo(basePath);
  if (prefix != null) relPath = new Path(prefix).join(relPath);
  var varName = relPath.toString().toUpperCase().replaceAll('/', '_').replaceAll('.', '_');
  var dstPath = cachePath.join(relPath);
  
  print('  $relPath');
  
  createDirectory(dstPath.directoryPath);
  var out = new File.fromPath(dstPath).openOutputStream();
  try {
    out.writeString('part of cache;\n\n');
    out.writeString('const $varName = """');
    out.writeString(oldSrc
        .replaceAll('\\', '\\\\')
        .replaceAll('\$', '\\\$')
        .replaceAll('"""', '\\"\\"\\"'));
    out.writeString('""";\n');
  } finally {
    out.close();
  }
  
  cacheDirectives.add('part "$relPath";\n');
  cacheSource.add('  "cache://$relPath" : $varName,\n');
  return oldSrc;
}

/// Scan the source for directives and extract relative paths of sourced files
List<Path> extractParts(String source) {
  var paths = new List<Path>();
  var start = 0;
  while (true) {
    start = source.indexOf('#source', start);
    if (start == -1) return paths;
    start = source.indexOf('"', start) + 1;
    var end = source.indexOf('"', start);
    paths.add(new Path(source.substring(start, end)));
  }
}

/// Verify input directory and create output directory
void setup() {
  print('current directory : $currentPath');

  print('dart sdk lib directory : $sdkPath');
  var libDir = new Directory.fromPath(sdkPath);
  if (!libDir.existsSync()) {
    print('>>> Dart SDK lib directory above does not exist');
    exit(1);
  }
  
  print('patch directory : $patchPath');
  var patchDir = new Directory.fromPath(patchPath);
  if (!patchDir.existsSync()) {
    print('>>> Patch directory above does not exist');
    exit(2);
  }

  print('lib cache directory : $cachePath');
  createDirectory(cachePath);
}

/// Recursively create the directory
void createDirectory(Path dirPath) {
  var dir = new Directory.fromPath(dirPath);
  if (dir.existsSync()) return;
  createDirectory(dirPath.directoryPath);
  dir.createSync();
}
